Warum mu� die Bitmap eigentlich 704 Pixel breit sein? Zuerst einmal mu� man sagen, da� die Bitmap generell breiter sein mu� als der sichtbare Bereich, weil wir "neu hereinkommende" Levelbereiche blockweise blitten und das in einem Bereich machen m�ssen, den man nicht sieht - sonst sieht das f�r den User gar nicht sch�n oder professionell aus. Der sichtbare Bereich betr�gt 320 Pixel in der Breite, d. h. 20 Bl�cke (SICHTBARE BREITE : BLOCKBREITE = 320 : 16 = 20). Es sind aber nicht immer nur 20 Bl�cke horizontal sichtbar, meistens sind es 21, n�mlich immer dann, wenn die Levelposition (d. h. die horizontale Position des Levelausschnittes in Pixel) nicht durch 16 (BLOCKBREITE) teilbar ist.
320 + 32 ist aber nur 352? War da nicht die Rede von 704 Pixeln? Nun, auf 704 Pixel kommen wir, wenn wir 352 mit 2 multiplizieren. Der wesentliche Trick von diesem Scroll-Algorithmus besteht n�mlich darin, da� unsere Bitmap doppelt so breit ist, wie sie eigentlich sein m��te. Die rechte H�lfte ist stets eine exakte Kopie der linken H�lfte. Deshalb wird beim Scrollen jeder zu blittende Block einmal in der linken und einmal in der rechten H�lfte geblittet. So ganz richtig mit der exakten Kopie ist das aber nur solange keine BOBs geblittet werden. Die m�ssen n�mlich zum Gl�ck nur einmal geblittet werden. Das aber nur nebenbei gesagt. Zu Beginn f�llen wir die beiden Bitmaph�lften mit dem anfangs sichtbaren Levelbereich:
Das rote Rechteck stellt den am Anfang aktuellen Bereich dar, der mit dem Copper dargestellt wird, wobei die ersten und letzten 16 Pixel ausgeblendet sind. Der User sieht also nur den vom gelben Rechteck eingegrenzten Bereich. Wenn wir nach rechts scrollen, dann verschiebt sich auch der aktuelle Bereich nach rechts. Wenn wir nach links scrollen, dann verschiebt er sich nach links. Den sichtbaren Bereich zu verschieben reicht nat�rlich nicht aus. Wir m�ssen auch die neu hereinkommenden Bl�cke in die Bitmap blitten. Gehen wir mal davon aus, da� wir nach rechts wollen. Am Anfang befinden sich in den beiden Bitmaph�lften die Blockspalten 0 bis 21, also die ersten 22 Spalten des Levels. Nach sp�testens 16 Pixeln (= Breite einer Spalte) Nach-Rechts-Scrollen m�ssen sich die Blockspalten 1 bis 22 in beiden Bitmaph�lften befinden. Da wir 16 Pixel lang Zeit dazu haben blitten wir die neue (22) Blockspalte nicht auf einmal, sondern schrittweise. 16 Pixel Zeit hei�t, da� wir sie in 16 Schritten blitten. Eine Blockspalte besteht aus 16 (BITMAPH�HE : BLOCKH�HE = 256 : 16 = 16) Bl�cken. Deshalb brauchen wir pro Schritt (= pro 1-Pixel-Scrolling) nur einen Block zu blitten. Welchen Schritt = welchen Block wir in einer bestimmten Situation (= LevelPosition) blitten m�ssen, berechnen wir, indem wir die LevelPositionX durch 16 (BLOCKBREITE) dividieren und uns den Rest anschauen. Der n�mlich bestimmt den Schritt. Den Rest berechnen wir in C entweder mit dem Modulo Operator (%), oder, weil 16 eine 2er Potenz ist mit einem bin�ren UND:
1) LevelPositionX % BLOCKBREITE2) LevelPositionX & (BLOCKBREITE -1)
Dabei erh�lt man Werte zwischen 0 und BLOCKBREITE - 1, in unserem Falle (d. h. Blockbreite 16 Pixel) also zwischen 0 und 15. Der sich in der Spalte ganz oben befindende Block ist Block 0, der ganz unten Block 15. Wir gingen davon aus, nach rechts zu scrollen, wollen also von LevelPositionX 0 zu LevelPositionX 1. Da der errechnete Schritt f�r Levelposition 1 gleich 1 ist, wir aber zuerst den obersten Block (= Block 0) blitten m�ssen bzw. wollen, erh�hen wir die Variable f�r LevelPositionX (in den Quelltexten mapposx genannt) erst am Ende der ScrollRechts() Funktion. So erhalten wir f�r das Scrolling von Levelposition 0 nach Levelposition 1 den Schrittwert 0, f�r das Scrolling von Levelposition 1 nach Levelposition 2 den Schrittwert 1 usw. So wird die Spalte nach und nach mit dem neuen Levelbereich gef�llt.
Ein St�ck weiter oben stand, da� die ersten und letzten 16 Pixel des aktuellen Bitmapbereiches ausgeblendet sind, soda� der User letzendlich den durch das gelbe Rechteck dargestellten Bereich sieht. Beim 1x FETCH Modus m�ssen wir sowieso die ersten 16 Pixel verdecken, um links den bereits erw�hnten Schiebeeffekt zu verhindern - das pa�t genau. Beim 2x FETCH Modus m�ssen wir aber 32 und beim 4x FETCH Modus sogar 64 Pixel verdecken. Um das Problem zu l�sen gibt es einen ganz einfachen Trick. Er funktioniert so, da� wir beim Blitten in die Bitmap einfach annehmen, diese bef�nde sich 16 Pixel = 2 Bytes (beim 2x FETCH Modus) bzw. 48 Pixel = 6 Bytes weiter hinten im Speicher. Deshalb wird der in den Quelltexten zum Blitten benutzten Bitmap-Variable frontbuffer nicht die richtige Adresse der Bitmap zugewiesen sondern die Addresse plus einem Offset (0 Bytes f�r 1x FETCH Modus - 2 f�r 2x Modus - 6 f�r 4x Modus). Wir d�rfen dann nicht vergessen, beim Anlegen der Bitmap 1 Zeile (wenn wenn wir die Bitmap mit AllocBitmap() erzeugen) bzw. 2 oder 6 Bytes (wenn wir die Bitmap mit AllocMem() erzeugen) mehr anzufordern. Durch die Verwendung dieses Offsets beim Zeichnen in die Bitmap sind keine weiteren �nderungen bei Benutzung der 2x bzw. 4x FETCH Modi notwendig, weil wir uns sozusagen den Verdeckungsbereich an die richtige Stelle schieben:
Das blaue Rechteck in den obigen Bildern stellt die zweite Bitmaph�lfte dar. Man beachte den "Wraparound" �ber den rechten Rand und den daraus resultierenden falbverf�lschten Bereich auf der linken Seite. Diese Farbverf�lschung - verursacht durch "Plane-Verschiebung" - sieht man nat�rlich nur, wenn man die echte Bitmapaddresse zur Anzeige verwendet und links nichts verdeckt.
Eine Frage ist nat�rlich noch offen. An welcher X-Position befindet sich die Nachf�llspalte in der Bitmap - wo m�ssen die neuen Bl�cke geblittet werden? Antwort: An der Stelle, wo sich die "hinauswandernde" Blockspalte befindet. Im obigen Beispiel w�re das die Spalte 0. Da w�hrend des Scrollens jeder Block zweimal geblittet wird, also einmal in der linken und einmal in der rechten der beiden Bitmaph�lften wandert der aktuell sichtbare Bereich beim Nach-Rechts-Scrollen langsam in Richtung der Nachf�llspalte der rechten Bitmaph�lfte. Sie wird gerade rechtzeitig fertig gef�llt sein, also noch bevor der User sie zu Gesicht bekommt. Denn sobald das geschieht ist die Nachf�llspalte bereits horizontal einen Block weiterger�ckt. In den Quelltexten enth�lt die Variable videoposx die Position des aktuellen Bitmapbereiches (rotes Rechteck). Daraus errechnen wir die Position in der Bitmap, an der sich die Nachf�llspalte befindet, d. h. wo wir die Bl�cke blitten m�ssen. Dazu gibt es wieder eine langsame (1) und eine schnelle (2) Methode, wobei letztere wieder nur dann funktioniert wenn BLOCKBREITE ein 2er Potenz ist:
1) x = videoposx - (videoposx % BLOCKBREITE)Diese Formel errechnet die Position in der linken Bitmaph�lfte. F�r den Blit in die rechte Bitmaph�lfte addieren wir einfach HALBEBITMAPBREITE, d. h. 352.2) x = videoposx & ~(BLOCKBREITE - 1)
Das nach Links-Scrollen geschieht analog, mit dem Unterschied, da� wir die LevelPositionX nicht am Ende der Scroll-Funktion �ndern, sondern am Anfang. Wir verwenden sozusagen Pre-Dekrement im Gegensatz zum Post-Inkrement beim Nach-Rechts-Scrollen. Der Grund ist ganz einfach. Stellen wir uns vor, wir sind von LevelPosition 0 nach LevelPosition 1, also 1 Pixel nach rechts, gescrollt. Das Ergebnis ist, da� Block 0 (ganz oben) der Nachf�llspalte einen rechts hereinkommenden Block, genauer gesagt Block (22,0), d. h. der 23. Block in der obersten Reihe des Levels, enth�lt - im folgenden Bild als ein gelbes Rechteck mit roter Umrandung dargestellt:
Wir befinden uns also an Levelposition 1 und wollen wieder zur�ck nach Levelposition 0, d. h. der gelb-rote Block mu� weg und mit Block (0,0) ersetzt werden. Aus Levelposition 1 errechnet sich der Schritt 1, was falsch ist, weil das bedeuten w�rde den Block (0,1) unter dem gelb-roten Block zu blitten. Wenn wir die Levelposition aber am Anfang der ScrollLinks() Funktion �ndern, dann errechnet sich der Schritt aus Levelposition 0 und wir erhalten als Ergebnis 0 (0 % 16 = 0). Das hei�t dann f�r die Scroll-Funktion Block (0,0) �ber das gelb-rote Rechteck zu blitten - genau das was wir wollen :-)
Etwas ganz wichtiges h�tte ich fast vergessen. Wie bereits gesagt, wird der "aktuelle" und sichtbare Bitmapbereich (rotes Rechteck im vorvorletztem Bild) beim Scrollen analog zur Scroll-Richtung verschoben. Was passiert aber, wenn wir so weit nach rechts gescrollt haben, da� der Bereich Gefahr l�uft �ber die Bitmapgrenzen hinauszulaufen? Das passiert ja schon nach 352 Pixeln. Ganz einfach. Wenn wir so weit nach rechts gescrollt sind, dann setzen wir den aktuellen und damit automatisch auch den sichtbaren Bitmapbereich wieder an den Anfang der Bitmap (Position 0). Das geht deshalb, weil die linke und rechte Bitmaph�lfte ja genau gleich ausschauen. Und wenn der Bereich droht, links �ber die Bitmapgrenze hinauszuscrollen, dann setzen wir ihn einfach an das rechte Maximum (Position 351). Ganz konkret hei�t das, da� sich die Position des "aktuellen" Bitmapbereichs durch folgende Formel errechnet:
videoposx = LevelPositionX % HALBEBITMAPBREITE, d. h.
videoposx = LevelPositionX % 352
Noch einige Bemerkungen zum Quelltext des Demoprogrammes. Wenn die Variable mapposx, die die Levelposition X in Pixeln enth�lt, auf 0 steht, dann sieht der User den Levelbereich ab Position BLOCKBREITE (16), weil die ersten BLOCKBREITE (16) Pixel stets verdeckt sind. Der User sieht also den Bereich:
(mapposx + BLOCKWIDTH,0) - (mapposx + BLOCKWIDTH + SCREENWIDTH - 1,SCREENHEIGHT - 1)
Das hei�t, da� die erste Blockspalte der Leveldatei nie sichtbar ist. Will man in die Bitmap etwas hineinblitten, z. B. Blitter Objekte (BOBs), dann mu� man das im aktuell sichtbaren Bitmapbereich machen. Dieser Bereich ist:
(videoposx + BLOCKWIDTH,0) - (videoposx + BLOCKWIDTH + SCREENWIDTH - 1,SCREENHEIGHT - 1)
Man darf die Start-X Koordinate (videoposx + BLOCKWIDTH) und die End-X Koordinate (videoposx + BLOCKWIDTH + SCREENWIDTH - 1) auf ein Vielfaches von BLOCKWIDTH auf- bzw. abrunden, soda� der Bereich, in den geblittet werden darf insgesamt SCREENWIDTH + BLOCKWIDTH Pixel breit ist:
blitbereich_strtx = (videoposx + BLOCKWIDTH) & ~(BLOCKWIDTH - 1)blitbereich_endex = blitbereich_strtx + SCREENWIDTH + BLOCKWIDTH - 1
Grundlage f�rs Blitten mu� immer die Variable frontbuffer sein, weil diese in den 2x und 4x FETCH Modi nicht identisch mit der echten Bitmapadresse ist!